package com.aepryus.loom;

import java.util.*;
import com.aepryus.util.*;

public abstract class DomainPort {
	private static Map<String,Domain> cache = Collections.synchronizedMap(new HashMap<String,Domain>());
	private static TallyMap tally = new TallyMap();
	private static Map<String,Map<String,Domain[]>> queryCache = new HashMap<String,Map<String,Domain[]>>();

// Utility =====================================================================
	private static String getKey(String domain,long id) {
		StringBuffer sb = new StringBuffer();
		sb.append(domain);
		sb.append("|");
		sb.append(id);
		return sb.toString();
	}
	private static String getKey (Domain domain) {
		return getKey(domain.getDomain(),domain.getID());
	}

// Cache =======================================================================
	public static Domain selectFromCacheByID (String name,long id) {
		return cache.get(getKey(name,id));
	}
	private static synchronized void bind (Domain domain) {
		domain.bind();
		cache.put(getKey(domain),domain);
		tally.increment(domain.getDomain());
	}
	static synchronized void unbind (Domain domain) {
		domain.unbind();
		cache.remove(getKey(domain));
		tally.decrement(domain.getDomain());
		clearQueryCache(domain.getDomain());
	}
	private synchronized static boolean isBound (Domain domain) {
		return cache.containsKey(getKey(domain));
	}
	public synchronized static void clearCache () {
		synchronized (Runner.transKey) {
			while (Runner.taskCount() != 0) {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					Log.print(e);
				}
			}
			cache.clear();
			tally.clear();
			clearQueryCache();
		}
	}

// Query Cache =================================================================
	private static Domain[] getQueryCache (String name,String where,String sort) {
		Map queries = queryCache.get(name);
		if (queries == null) return null;
		return (Domain[])queries.get(where+"||"+sort);
	}
	private static void putQueryCache (String name,String where,String sort,Domain[] domains) {
		// Longer term it would be nice to parse the FROM statement in order to determine which
		// Domains any given query is effected by, which would allow for a more accuracte query
		// invalidation process.  This would also allow for SQL.query() to be cashed.
		if (where != null && where.indexOf(" FROM ") != -1) return;

		Map<String,Domain[]> queries = queryCache.get(name);
		if (queries == null) {
			queries = new HashMap<String,Domain[]>();
			queryCache.put(name,queries);
		}
		queries.put(where+"||"+sort,domains);
	}
	static void clearQueryCache (String name) {
		queryCache.remove(name);
	}
	private static void clearQueryCache () {
		queryCache = new HashMap<String,Map<String,Domain[]>>();
	}

// Public ======================================================================
	public static Domain create (String name) throws PersistenceException {
		Domain domain = Controller.controller.getPersist(name).create();

        bind(domain);
		domain.create();

		return domain;
	}
	public static Domain selectByID (String name,long id) throws PersistenceException {
		Domain domain = selectFromCacheByID(name,id);

		if (domain == null) {
			domain = Controller.controller.getPersist(name).selectByID(id);
		    if (domain != null) {
				domain.read();
				bind(domain);
			}
		}

		return domain;
	}
	public static Domain selectOne (String name,String where) throws PersistenceException {
		Domain[] domains = select(name,where);
		if (domains.length == 0) return null;
		if (domains.length == 1) return domains[0];
		throw new RuntimeException("selectOne query returned multiple rows");
	}
	public static Domain[] selectAll (String name) throws PersistenceException {
		return select(name,"");
	}
	public static Domain[] select (String name,String where) throws PersistenceException {
		return select(name,where,null);
	}
	public static Domain[] select (String name,String where,String orderBy) throws PersistenceException {
		Domain[] domains = getQueryCache(name,where,orderBy);
		if (domains != null) return domains;

		domains = Controller.controller.getPersist(name).select(where,orderBy);
		List<Domain> list = new AepList<Domain>();

		for (Domain domain : domains) {
			if (isBound(domain)) {
				domain = selectFromCacheByID(domain.getDomain(), domain.getID());
/*	The following line was put in, in order to not return Domain objects which had
	already been deleted previously in the current Task.  This seems like a reasonable
	thing to do, however this still doesn't take into account new Domain objects
	that may have been created that would be returned by the query or existing Domain
	objects whose data has changed in a manner that would effect whether or not
	they now would or now would not be selected.

	At this point the reason I am leaving this in is because this instance is easy
	to detect and the other 2 instances are not easy detect short of writing a
	cache query mechanism.  Although, that may give a developer a false sense of
	security and it may in fact be a better idea to remove this line so that
	this method fails consistently. - jjc:11/20/2003 */
				if (domain.getStatus() != Domain.DELETE)
					list.add(domain);
			} else {
				domain.read();
				bind(domain);
				list.add(domain);
			}
		}

		domains = list.toArray(domains);

		putQueryCache(name,where,orderBy,domains);

		return domains;
	}

// Status ======================================================================
	public static synchronized TallyMap getTally () {
		return (TallyMap)tally.clone();
	}
}

